package fmtlog

import (
	"fmt"
	"os"
	"runtime"
	"strings"
	"time"
	"sync"
	"sample/pkg/timer"
    "io"
)

const (
    LOG_CHAN_MAX    = 10240
    LOG_FILE_MAX    = 1024 * 1024 * 1024 // 1G
    SYNC_BUFFER_MAX = 4 * 1024
)

// severity identifies the sort of log: info, warning etc.
// These constants identify the log levels in order of increasing severity.
const (
	DEBUG_LOG  = iota
	INFO_LOG
	WARNING_LOG
	ERROR_LOG
	FATAL_LOG
	MAX_SERVERITY
)

var level_name = []string{
	DEBUG_LOG:   "DEBUG",
	INFO_LOG:    "INFO",
	WARNING_LOG: "WARN",
	ERROR_LOG:   "ERROR",
	FATAL_LOG:   "FATAL",
}

// flushSyncWriter is the interface satisfied by logging destinations.
type syncWriter interface {
	Flush() error
	Sync() error
	io.Writer
}

// loggingT collects all the global state of the logging setup.
type FmtLog struct {
                      // freeList is a list of byte buffers, maintained under freeListMu.
    buflst *buffer
    mu     sync.Mutex

    writer syncWriter // file holds writer for log.
    wchan  chan []byte

    lv     int
    fname  string

    max    int64
    debug  bool
}

func NewFmtLog(lv int, name string, d bool) *FmtLog {
    l := &FmtLog{}
    l.lv = lv
    l.wchan = make(chan []byte, LOG_CHAN_MAX)
	l.fname = name
    l.max = LOG_FILE_MAX
    l.debug = d

    go l.output()
    return l
}

func (l *FmtLog) printf(s int , format string, args ...interface{}) {
	if l.lv > s {
		return
	}

	buf := l.getBuffer()
    defer l.putBuffer(buf)

	l.header(buf, s, 0)
	fmt.Fprintf(buf, format, args...)
	if buf.Bytes()[buf.Len()-1] != '\n' {
		buf.WriteByte('\n')
	}

    if len(l.wchan) < LOG_CHAN_MAX {
	    l.wchan <- buf.Bytes()
    } else {
        fmt.Fprintf(os.Stderr, "log msg full log chan\n")
    }
}

func (l *FmtLog) println(s int, args ...interface{}) {
	if l.lv > s {
		return
	}

	buf := l.getBuffer()
    defer l.putBuffer(buf)

	l.header(buf, s, 0)
	fmt.Fprintln(buf, args...)
	if len(l.wchan) < LOG_CHAN_MAX {
		l.wchan <- buf.Bytes()
	} else {
        fmt.Fprintf(os.Stderr, "log msg full log chan\n")
	}
}

// getBuffer returns a new, ready-to-use buffer.
func (l *FmtLog) getBuffer() *buffer {
	l.mu.Lock()
	b := l.buflst
	if b != nil {
		l.buflst = b.next
	}
	l.mu.Unlock()
	if b == nil {
		b = new(buffer)
	} else {
		b.next = nil
		b.Reset()
	}
	return b
}

// putBuffer returns a buffer to the free list.
func (l *FmtLog) putBuffer(b *buffer) {
	if b.Len() >= 256 { // Let big buffers die a natural death.
		return
	}
	l.mu.Lock()
	b.next = l.buflst
	l.buflst = b
	l.mu.Unlock()
}

func (l *FmtLog) header(buf *buffer, lv int, depth int) {
	now := time.Now()
	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
	// It's worth about 3X. Fprintf is hard.
	year, month, day := now.Date()
	hour, minute, second := now.Clock()
	// 2015-12-04 10:52:36 pid [file:line] INFO -
	buf.nDigits(4, 0, year, byte('0'))
	buf.tmp[4] = '-'
	buf.twoDigits(5, int(month))
	buf.tmp[7] = '-'
	buf.twoDigits(8, day)
	buf.tmp[10] = ' '
	buf.twoDigits(11, hour)
	buf.tmp[13] = ':'
	buf.twoDigits(14, minute)
	buf.tmp[16] = ':'
	buf.twoDigits(17, second)
	buf.tmp[19] = ' '
	pid := os.Getpid()
	n := buf.someDigits(20, pid)
	buf.tmp[n+20] = ' '
	buf.Write(buf.tmp[:n+21])
	d := level_name[lv]
	buf.WriteString(d)
    if l.debug {
        _, file, line, ok := runtime.Caller(3 + depth)
        if !ok {
            file = "___"
            line = 1
        } else {
            slash := strings.LastIndex(file, "/")
            if slash >= 0 {
                file = file[slash + 1:]
            }
        }
        buf.tmp[0] = ' '
        buf.tmp[1] = '['
        buf.Write(buf.tmp[:2])
        buf.WriteString(file)
        buf.tmp[0] = ':'
        n = buf.someDigits(1, line)
        buf.tmp[n + 1] = ']'
        buf.tmp[n + 2] = ' '
    }

	buf.tmp[n+3] = '-'
	buf.tmp[n+4] = ' '
	buf.Write(buf.tmp[:n+5])
}

// output writes the data to the log files and releases the buffer.
func (l *FmtLog) output() {
    write_tm := timer.NowSec
    for data := range l.wchan {
        if l.writer == nil {
			l.writer = NewFmtWriter(l.fname, l.max) //l lv path+name, max_sz
        }

        _, e := l.writer.Write(data)
        if e != nil {
            l.writer = nil
            continue
        }

        if l.debug || timer.NowSec > write_tm + 10 { // 10 second per sync
            write_tm = timer.NowSec
            l.writer.Flush()
            l.writer.Sync()
        }
    }

    if l.writer != nil {
        l.writer.Flush()
        l.writer.Sync()
    }
}

// exit is called if there is trouble creating or writing log files.
func (l *FmtLog) Close() {
    close(l.wchan)
}

func (l *FmtLog) Debug(args ...interface{}) {
	l.println(DEBUG_LOG, args...)
}

func (l *FmtLog) Debugf(format string, args ...interface{}) {
	l.printf(DEBUG_LOG, format, args...)
}

func (l *FmtLog) Info(args ...interface{}) {
	l.println(INFO_LOG, args...)
}

func (l *FmtLog) Infof(format string, args ...interface{}) {
	l.printf(INFO_LOG, format, args...)
}

func (l *FmtLog) Warning(args ...interface{}) {
	l.println(WARNING_LOG, args...)
}

func (l *FmtLog) Warningf(format string, args ...interface{}) {
	l.printf(WARNING_LOG, format, args...)
}

func (l *FmtLog) Error(args ...interface{}) {
	l.println(ERROR_LOG, args...)
}

func (l *FmtLog) Errorf(format string, args ...interface{}) {
	l.printf(ERROR_LOG, format, args...)
}

func (l *FmtLog) Fatal(args ...interface{}) {
	l.println(FATAL_LOG, args...)
}

func (l *FmtLog) Fatalf(format string, args ...interface{}) {
	l.printf(FATAL_LOG, format, args...)
}


var defaultlog = NewFmtLog(DEBUG_LOG, "system_default", true)

func Init(lv int, name string, d bool) {
	defaultlog = NewFmtLog(lv, name, d)
}

func Debug(args ...interface{}) {
	defaultlog.println(DEBUG_LOG, args...)
}

func Debugf(format string, args ...interface{}) {
	defaultlog.printf(DEBUG_LOG, format, args...)
}

func Info(args ...interface{}) {
	defaultlog.println(INFO_LOG, args...)
}

func Infof(format string, args ...interface{}) {
	defaultlog.printf(INFO_LOG, format, args...)
}

func Warning(args ...interface{}) {
	defaultlog.println(WARNING_LOG, args...)
}

func Warningf(format string, args ...interface{}) {
	defaultlog.printf(WARNING_LOG, format, args...)
}

func Error(args ...interface{}) {
	defaultlog.println(ERROR_LOG, args...)
}

func Errorf(format string, args ...interface{}) {
	defaultlog.printf(ERROR_LOG, format, args...)
}

func Fatal(args ...interface{}) {
	defaultlog.println(FATAL_LOG, args...)
}

func Fatalf(format string, args ...interface{}) {
	defaultlog.printf(FATAL_LOG, format, args...)
}

func Test() {
	Debug("DEBUG LOG")
	Info("INOF LOG")
	Warning("WARNING LOG")
	Error("ERROR LOG")
	Fatal("FATAL LOG")
}
